package com.zengdada.authority.annotation;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zengdada.authority.config.security.intercept.AuthorityUtil;
import com.zengdada.authority.exception.RoutineException;
import com.zengdada.authority.model.SysFunkLog;
import com.zengdada.authority.service.ServiceFunkLogAuthorityService;
import com.zengdada.authority.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @author zengshunan
 * @contact 1261438511@qq.com
 * @date 2019-03-29 22:57
 */

@Slf4j
public abstract class BaseLogAop<T extends Annotation> {

    protected static ObjectMapper objectMapper;

    static {
        objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    @Autowired
    @Qualifier("serviceFunkLogAuthorityServiceImpl")
    protected ServiceFunkLogAuthorityService logService;


    @Autowired
    protected AuthorityUtil authorityUtil;

    @Value("${authority.application.id}")
    protected String APPLICATION_ID;
    protected Class<T> actualClass;

    @PostConstruct
    public void initMethod() {
        // 获取泛型类型
        Type[] arguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
        if (arguments == null || arguments.length == 0) {
            log.error("{}没有设置泛型", this.getClass());
            return;
        }
        actualClass = (Class<T>) arguments[0];
    }

    protected Object analysis(ProceedingJoinPoint pjp) {
        if (pjp.getThis() instanceof SysLogNotIntercept) {
            try {
                return pjp.proceed();
            } catch (Throwable e) {
                if (e.getClass() == RoutineException.class) {
                    throw new RoutineException(e.getMessage());
                }
                if (e.getClass() == NullPointerException.class) {
                    throw new NullPointerException(e.getMessage());
                }
                throw new RuntimeException(e);
            }
        }
        Object result;
        SysFunkLog log = new SysFunkLog();
        log.setApplicationId(APPLICATION_ID);
        long startTime = 0;
        setCreateUser(log);
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        T annotation = method.getAnnotation(actualClass);
        setAnnotationMsg(annotation, log);
        if (!log.getType().equals(SysLogType.OTHER.name())) {
            try {
                ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
                HttpServletRequest request = attr.getRequest();
                log.setIp(HttpUtils.getIp(request));
                log.setMethed(request.getMethod());
            } catch (IllegalStateException e) {
                //
            }
        }
        try {
            //获取方法参数
            Object[] params = pjp.getArgs();
            analysisParams(params, log);
            setSourceFunk(pjp, log);
            log.setFunk(signature.toString());
            //获取开始时间
            startTime = System.nanoTime();
            //执行目标对象的方法 china
            result = pjp.proceed();
            log.setResult(result == null ? null : objectMapper.writeValueAsString(result));
            log.setSuccess(1);
//            System.out.println("环绕通知---后置通知");
        } catch (Throwable e) {
            log.setSuccess(0);
            String msg = e.getMessage();
            log.setErrorMsg((msg != null ? msg.substring(0, msg.length() > 150 ? 149 : msg.length()) : ""));
            if (e.getClass() == RoutineException.class) {
                throw new RoutineException(e.getMessage());
            }
            if (e.getClass() == NullPointerException.class && (msg == null || "".equals(msg))) {
                log.setErrorMsg("空指针异常！");
            }
            throw new RuntimeException(e);
        } finally {
//            System.out.println("环绕通知---最终通知");
            log.setUseTime(System.nanoTime() - startTime);
            logService.add(log);
        }
        return result;
    }

    protected abstract void setAnnotationMsg(T annotation, SysFunkLog log);

    protected void setCreateUser(SysFunkLog log) {
        try {
            log.setCreateUser(authorityUtil.getLoginUser().getName());
        } catch (Exception e) {
            log.setCreateUser(e.getMessage());
        }
    }

    protected void analysisParams(Object[] params, SysFunkLog log) {
        if (null != params && params.length > 0) {
            StringBuilder buffer = new StringBuilder();
            for (Object object : params) {
                try {
                    buffer.append(objectMapper.writeValueAsString(object)).append(",");
                } catch (JsonProcessingException e) {
                    buffer.append(object != null ? object.getClass().getSimpleName() : null).append(",");
                }
            }
            if (buffer.charAt(buffer.length() - 1) == ',') {
                buffer.deleteCharAt(buffer.length() - 1);
            }
            log.setParams(buffer.toString().replaceAll("\"password\":\".+?\"", "\"password\":\"***\"")
                    .replaceAll("\"pass\":\".+?\"", "\"pass\":\"***\""));
        }
    }


    protected void setSourceFunk(ProceedingJoinPoint pjp, SysFunkLog log) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        log.setFunk(signature.toString());
        Class<?> signatureMethodDeclaringClass = signature.getMethod().getDeclaringClass();
        if (AopUtils.isAopProxy(pjp.getThis())) {
            Class<?> targetClass = ((Advised) pjp.getThis()).getTargetSource().getTargetClass();
            if (targetClass != null) {
                log.setFunk(signature.toString().replaceAll(signatureMethodDeclaringClass.getName(), targetClass.getName()));
            }
        }
    }

}
